
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author amerer & dleang
 */
public class Joueur implements InterfaceJoueur {
    
    private Couleur maCouleur;
    private TypeJoueur type;
    private Couleur matricePlateau[][];
    private Plateau plateau;
    
    public Joueur(Couleur maCouleur, TypeJoueur type, InterfacePlateau plateau) {
        this.maCouleur = maCouleur;
        this.type = type;
        this.matricePlateau = plateau.getMatricePlateau();
        this.plateau = (Plateau)plateau;
    }

    @Override
    /**
     * Calcule le meilleur coup à jouer
     * @return Le meilleur coup à jouer
     */
    public Coup joue() {
        List<Coup> listCoupsPossibles = new ArrayList<Coup>();
        Coup coupAJouer = null;
        int nb = 0;
        int nbMax = 0;
        Coup meilleurCoup = new Coup(0, 0); 

        listCoupsPossibles = getCoupsPossibles();

        // Cette boucle for permet de calculer le meilleur coup
        // c'est-à-dire celui qui va retourner le plus de pions adverses
        for(int i = 0; i < listCoupsPossibles.size(); i++)
        {
            nb = calculerNombrePionsRetournes(listCoupsPossibles.get(i), getCouleurAdverse());

            if(nb > nbMax)
            {
                nbMax = nb;
                meilleurCoup = listCoupsPossibles.get(i);
            }
        }

        System.out.println("Meilleur coup : Ligne : " + meilleurCoup.getLigne() + ", Colonne : " + meilleurCoup.getColonne());

        coupAJouer = meilleurCoup;

        // Décommenter pour trouver un coup aléatoirement
        /*
        if(!listCoupsPossibles.isEmpty())
        {
            Random coupAleatoire = new Random();
            coupAJouer = listCoupsPossibles.get(coupAleatoire.nextInt(listCoupsPossibles.size()));
        }
        */
        
        return coupAJouer;
    }

    @Override
    public TypeJoueur getType() {
        return this.type; 
    }

    @Override
    public Couleur getCouleur() {
        return this.maCouleur; 
    }

    /**
     * Récupère la couleur de l'adversaire
     * @return La couleur de l'adversaire
     */
    public Couleur getCouleurAdverse()
    {
        if(maCouleur.equals(Couleur.BLANC))
            return Couleur.NOIR;
        else return Couleur.BLANC;
    }

    /**
     * Récupère la liste de tous les coups possibles pour le pion courant
     * @return La liste de tous les coups possibles
     */
    private ArrayList<Coup> getCoupsPossibles() {
        ArrayList<Coup> listCoupsPossibles = new ArrayList<Coup>();
        ArrayList<Coup> listCoupsAdversaire = getListPionsAdversaire();

        for(int k = 0; k < listCoupsAdversaire.size(); k++)
        {
            for(int i = listCoupsAdversaire.get(k).getLigne()-1; i <= listCoupsAdversaire.get(k).getLigne()+1; i++){
                // Pour chaque colonne
                for(int j = listCoupsAdversaire.get(k).getColonne()-1; j <= listCoupsAdversaire.get(k).getColonne()+1; j++){
                    Coup currentCoup = new Coup(i, j);
                    // Si le coup actuel n'est pas un coup adverse
                    if(!(currentCoup.getColonne() == listCoupsAdversaire.get(k).getColonne()
                            && currentCoup.getLigne() == listCoupsAdversaire.get(k).getLigne())){
                        // Peut-on jouer le coup ?
                        if(isCoupPossible(listCoupsAdversaire.get(k), currentCoup)){
                            listCoupsPossibles.add(currentCoup);
                        }
                    }
                }
            }
        }

        System.out.print("Coups possibles : ");
        for(Coup coup : listCoupsPossibles){
            System.out.print("(" + coup.getLigne() + "," + coup.getColonne() + ") ");
        }
        System.out.print("\n");

        return listCoupsPossibles;
    }

    /**
     * Récupère la liste des pions adverses
     * @return La liste des pions
     */
    private ArrayList<Coup> getListPionsAdversaire() {
        ArrayList<Coup> listPionsAdversaire = new ArrayList<Coup>();

        Couleur[][] tmpPlateau = this.plateau.getMatricePlateau();

        Couleur couleurAdversaire; 
        
        if(this.maCouleur.equals(Couleur.NOIR))
            couleurAdversaire = Couleur.BLANC; 
        else
            couleurAdversaire = Couleur.NOIR; 

        for(int i = 0; i < 8; ++i){
            for(int j = 0; j < 8; ++j){
                if(tmpPlateau[i][j].equals(couleurAdversaire)){
                    listPionsAdversaire.add(new Coup(i, j));
                }
            }
        }
        return listPionsAdversaire;
    }

    /**
     * Teste si un coup est possible par rapport à un coup adverse
     * @param coupAdversaire Le coup adverse
     * @param currentCoup Le coup à tester
     * @return Vrai si le coup est possible
     */
    private boolean isCoupPossible(Coup coupAdversaire, Coup currentCoup) {
        boolean isPossible = false;

        //si le coup est dans le plateau
        if(isCoupDansPlateau(currentCoup)){

            // Copie du plateau
            Couleur[][] tmpPlateau = this.plateau.getMatricePlateau();

            // Si le coup n'a pas déjà été joué
            if(tmpPlateau[currentCoup.getLigne()][currentCoup.getColonne()].equals(Couleur.VIDE)){

                Couleur couleurAdversaire = getCouleurAdverse();

                // Numéro de la ligne du prochain coup
                int numLigneProchain = coupAdversaire.getLigne() + (coupAdversaire.getLigne() - currentCoup.getLigne());
                // Numéro de la colonne du prochain coup
                int numColonneProchain = coupAdversaire.getColonne() + (coupAdversaire.getColonne() - currentCoup.getColonne());
                // Coup futur
                Coup coupAJouer = new Coup(numLigneProchain, numColonneProchain);

                //si le prochain coup est toujours dans le plateau
                if(isCoupDansPlateau(coupAJouer)){

                    int numLigneAdversaire   = coupAdversaire.getLigne();
                    int numColonneAdversaire = coupAdversaire.getColonne();

                    while(isCoupDansPlateau(new Coup(numLigneProchain, numColonneProchain))
                            && tmpPlateau[numLigneProchain][numColonneProchain].equals(couleurAdversaire)){

                        int l = numLigneProchain;
                        int c = numColonneProchain;

                        //indice de ligne du prochain coup
                        numLigneProchain = numLigneProchain + (numLigneProchain - numLigneAdversaire);
                        //indice de colonne du prochain coup
                        numColonneProchain = numColonneProchain + (numColonneProchain - numColonneAdversaire);

                        numLigneAdversaire = l;
                        numColonneAdversaire = c;
                    }

                    //si le prochain coup est en dehors du plateau
                    if(!isCoupDansPlateau(new Coup(numLigneProchain, numColonneProchain))){
                        isPossible = false;
                    } else {
                        //si le prochain coup est de ma couleur : gagné
                        if(tmpPlateau[numLigneProchain][numColonneProchain].equals(this.maCouleur)){
                            isPossible = true;
                        } else {
                            //si le prochain coup est de la couleur vide : perdu
                            if(tmpPlateau[numLigneProchain][numColonneProchain].equals(Couleur.VIDE)){
                                isPossible = false;
                            }
                        }
                    }
                }
            }
        }

        return isPossible;
    }

    /**
     * Teste si le coup a déjà été effectué
     * @param coup Le coup à tester
     * @return Vrai si le coup a déjà été joué
     */
    private boolean isCoupDansPlateau(Coup coup) {
        return coup.getColonne()< 8 && coup.getColonne()>=0
                && coup.getLigne()< 8 && coup.getLigne()>=0;
    }

    /**
     * Teste l'égalité de deux coups
     * @param currentCoup Le premier coup
     * @param get Le deuxième coup
     * @return Vrai si les deux coups sont identiques
     */
    private boolean coupsEgaux(Coup currentCoup, Coup get) {
        boolean areEquals = false;

        if (currentCoup.getColonne() == get.getColonne() && currentCoup.getLigne() == get.getLigne())
        {
            areEquals = true;
        }

        return areEquals;
    }

    /**
     * Calcule le nombre de pions adverses qui seront retournés après un coup
     * @param coup Le coup dont on cherche le nombre de pions adverses affectés
     * @param couleurAdversaire La couleur de l'adversaire
     * @return Le nombre de pions retournés
     */
    private int calculerNombrePionsRetournes(Coup coup, Couleur couleurAdversaire) {
        int ligneCoup;
        int colonneCoup;

        int c = 0;
        int d = 0;

        int nbPionsRetournes     = 0;
        int nbPionsRetournesTemp = 0;

        boolean estCaseVide = false;
        boolean hasBroken   = false;

        ligneCoup = coup.getLigne();
        colonneCoup = coup.getColonne();

        // On fait le tour du pion
        for (int a = -1; a <= 1; a++) {
            for (int b = -1; b <= 1; b++) {

                // Si on rencontre un pion adverse alors on compte le nombre de pions adverses retournés si c'est le cas
                if((ligneCoup + a == -1) || (ligneCoup + a == 8)
                        || (colonneCoup + b == -1) || (colonneCoup + b == 8))
                {
                    hasBroken = true;
                    break;
                }

                if (plateau.getMatricePlateau()[ligneCoup + a][colonneCoup + b].equals(couleurAdversaire)) {
                    c = a;
                    d = b;

                    while (ligneCoup + c < 8 && colonneCoup + d < 8 && ligneCoup + c >= 0 && colonneCoup + d >= 0) {

                        // Si on rencontre sa couleur, alors pas de retournement
                        if (plateau.getMatricePlateau()[ligneCoup + c][colonneCoup + d].equals(maCouleur)) {
                            break;
                        }

                        // Idem si couleur vide
                        else if (plateau.getMatricePlateau()[ligneCoup + c][colonneCoup + d].equals(Couleur.VIDE)) {
                            estCaseVide = true;
                            break;
                        }

                        // On incrémente le nombre de pions retournés
                        nbPionsRetournesTemp++;

                        c += a;
                        d += b;
                    }

                    // Si on avait rencontré une case vide, alors on ne retourne aucun pion
                    if (estCaseVide) {
                        nbPionsRetournesTemp = 0;
                        estCaseVide = false; 
                    }

                    nbPionsRetournes += nbPionsRetournesTemp;
                }
            }

            if (hasBroken)
            {
                hasBroken = false;
                break; 
            }
        }

        nbPionsRetournesTemp = 0;

        return nbPionsRetournes;
    }
}
